package affd.logic;

import java.util.ArrayList;
import java.util.HashSet;

/**
 * A SocialSecurityNumberAnonymizer is a implementation of the Anonymizer
 * to anonymize finnish social security numbers.
 *
 */
public class SocialSecurityNumberAnonymizer implements Anonymizer {
	
	public static final String MODIFICATION_CLASS = "ssnumber";
	
	public static final int LENGTH = 11;
	public static final int DATE_OF_BIRTH_INDEX = 0;
	public static final int DATE_OF_BIRTH_LENGTH = 6;
	public static final int CENTURY_INDEX = 6;
	public static final String[] VALID_CENTURIES = {"+", "-", "A"};
	public static final int NUMBER_INDEX = 7;
	public static final int NUMBER_LENGTH = 3;
	public static final int CHECK_INDEX = 10;
	public static final String[] VALID_CHECKS = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y"};

	private HashSet<String> ignoreList = null;
	
	/**
	 * Constructs a finnish social security number anonymizer.
	 * 
	 * @param sentences Sentences to be processed.
	 */
	public SocialSecurityNumberAnonymizer() {

	}
	
	/**
	 * 
	 * @param doNotDeleteList
	 */
	public void setIgnoreList(HashSet<String> doNotDeleteList) {
		this.ignoreList = doNotDeleteList;
	}
	
	/**
	 * Anonymizes social security numbers.
	 * 
	 * @throws LogicException
	 */
	public void process(ArrayList<Sentence> sentences) throws LogicException {
		// Process only unknown tokens
		for(Sentence sentence : sentences) {
			ArrayList<Token> tokens = sentence.getTokens();
			for(Token token : tokens) {
				// Check token
				if(isSocialSecurityNumber(token)) {
					if (ignoreList == null || (ignoreList != null && !ignoreList.contains(token.getContent()))) {
						anonymize(token);
					}
				}
			}
		}
	}
	
	/**
	 * Tests if the specified token is social security number.
	 * 
	 * @param token Token to be tested.
	 * @return True if the specified token is social security number otherwise false.
	 */
	private boolean isSocialSecurityNumber(Token token) {
		// Check token type and length
		if(token.getType() == Token.Type.UNKNOWN) {
			String content = token.getContent();
			if(content.length() == LENGTH) {
				if(isValidDateOfBirth(content) && isValidCentury(content) && isValidNumber(content) && isValidCheck(content)) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	/**
	 * Tests if the specified token contains valid date of birth number.
	 * 
	 * @param token Token to be tested.
	 * @return True if the specified token contains valid date of birth otherwise false.
	 */
	private boolean isValidDateOfBirth(String token) {
		for(int i = DATE_OF_BIRTH_INDEX; i < DATE_OF_BIRTH_LENGTH; i++) {
			if(!Character.isDigit(token.charAt(i))) {
				return false;
			}
		}
		
		return true;
	}

	/**
	 * Tests if the specified token contains valid century.
	 * 
	 * @param token Token to be tested.
	 * @return True if the specified token contains valid century otherwise false.
	 */
	private boolean isValidCentury(String token) {
		String centry = token.substring(CENTURY_INDEX, CENTURY_INDEX + 1);
		for(String valid : VALID_CENTURIES) {
			if(centry.equals(valid)) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Tests if the specified token contains id number.
	 * 
	 * @param token Token to be tested.
	 * @return True if the specified token contains valid id number otherwise false.
	 */
	private boolean isValidNumber(String token) {
		for(int i = NUMBER_INDEX; i < NUMBER_LENGTH; i++) {
			if(!Character.isDigit(token.charAt(i))) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Tests if the specified token contains valid check letter.
	 * 
	 * @param token Token to be tested.
	 * @return True if the specified token contains valid heck letter otherwise false.
	 */
	private boolean isValidCheck(String token) {
		String check = token.substring(CHECK_INDEX, CHECK_INDEX + 1);
		for(String valid : VALID_CHECKS) {
			if(check.equals(valid)) {
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Anonymizes the specified token.
	 * 
	 * @param token Token to be anonymized.
	 */
	public void anonymize(Token token) {		
		StringBuffer contentBuffer = new StringBuffer();
		for(int i = 0; i < token.getContent().length(); i++) {
			contentBuffer.append(Anonymizer.ANONYMIZED_LETTER);
		}
		String groupID = Token.generateGroupId();
		token.modifyContent(contentBuffer.toString(), MODIFICATION_CLASS, groupID);
	}

}
